package QianFeng09.ThreadDemo;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程同步
 * 1、多线程共享数据
 *      在多线程的操作中，多个线程有可能同时处理同一个资源，这就是多线程中的共享数据。I
 * 2、线程同步
 *      解决数据共享问题，必须使用同步，所谓同步就是指多个线程在同一个时间段内只能有一个线程执行指定代码，
 *      其他线程要等待此线程完成之后才可以继续执行。
 * 线程进行同步，有以下三种方法:
 * (1)同步代码块
 * synchronized(要同步的对象){
 *      要同步的操作;
 * (2)同步方法
 * public synchronized void method(){
 *      要同步的操作;
 * (3) Lock (ReentrantLock)
 *
 * 3、同步准则
 * 当编写synchronized块时，有几个简单的准则可以遵循，这些准则在避免死锁和性能危险的风险方面大有帮助:
 * (1)使代码块保持简短。把不随线程变化的预处理和后处理移出synchronized块。
 *      多线程共享数据，会有安全问题，使用同步可以解决安全问题，但是会牺牲性能，所以同步代码块要简洁
 * (2)不要阻塞。如inputStream.read()。
 * (3)在持有锁的时候，不要对其它对象调用方法（同步方法）。
 **/
public class ThreadSynchronization {

    public static void main(String[] args) {
        MyRunnable5 myRunnable5 = new MyRunnable5();
        //两个售票窗口
        //没使用同步则两个线程一起执行同一代码块导致线程不安全
        Thread t1 = new Thread(myRunnable5);
        Thread t2 = new Thread(myRunnable5);
        t1.start();
        t2.start();

    }
}

class MyRunnable5 implements Runnable {
    private int ticket = 10;//售票
    //private Object obj = new Object(); //同步锁
    @Override
    public void run() {
        for (int i = 0; i < 300; i++) {
            /*//方法一：需要同步的代码块
            synchronized (this) {//锁着，等待线程执行一个时间片段完后，另一个线程才有机会执行
                if (ticket > 0) {
                    ticket--;
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("你购买的票已剩余" + ticket + "张");
                }
            }*/
            method();//方法二 同样可以达到线程同步的方法
        }
    }

    //方法二  同步方法
    private synchronized void  method() {
        if (ticket > 0){
            ticket--;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("你购买的票已剩余" + ticket + "张");
        }
    }

    //方法三  Lock实现同步
    ReentrantLock lock = new ReentrantLock();//互斥锁
    //Lock实现同步
    private void  method2() {
        lock.lock();//锁
        if (ticket > 0){
            ticket--;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("你购买的票已剩余" + ticket + "张");
        }
        lock.unlock();//释放锁
    }
}
